package robocup.component.tactics;

import java.util.Arrays;
import java.util.LinkedList;

import robocup.component.ControllerNumbers;
import robocup.component.SConf;
import robocup.component.ControllerNumbers.PLAY_MODE;
import robocup.component.ControllerNumbers.STAMINA_LEVEL;
import robocup.component.actionfactories.BasicActionFactory;
import robocup.component.actionfactories.TurnNeckActionFactory;
import robocup.component.actions.TurnNeckAction;
import robocup.component.actions.Action.TYPE;
import robocup.component.worldobjects.Player;
import robocup.component.worldobjects.Player.ROLE;
import robocup.component.worldobjects.Player.UPDATE_MODE;

public class InterceptBallState
    extends AbstractState {

  private Player fastestOpponent;
  private int    myInterceptionTime;
  private int    opponentInterceptionTime;
  private int    secondFastestTeammateInterceptionTime;
  private int    fastestTeammateInterceptionTime;
  private int    bestInterceptionTime;
  
  /**
   * Simple state that defines ball-interception.
   * 
   * @param parent -
   *          the state tactic
   */
  public InterceptBallState(
      final StateEvaluation parent) {

    super(parent);
    this.state = STATES.INTERCEPT_BALL;
  }

  /**
   * Overwrites PreCondition, because it is extendes to if true, only if the
   * player is fastest player to ball!
   */
  @Override
  public boolean preCondition() {

    if (this.getWorld().getNumber() == 10 && PLAY_MODE.META_PENALTY_OWN
        .contains(this.getWorld().getPlayMode())) {
      if (!this.getPlayer().canKick(this.getWorld().getBallRef())) {
        return true;
      }
      else {
        return false;
      }
    }

    if (PLAY_MODE.META_PENALTY_OWN.contains(this.getWorld().getPlayMode())) {
      return false;
    }
    
    if (!this.checkPlayMode() || !this.getBall().isReliable() || this
        .getPlayer().canKick(this.getWorld().getBallRef()) || (this.getPlayer()
        .wasOffsideWhenPassed() && this.getPlayer().equals(
        this.getWorld().getSelfRef()))) {
      // if(this.getPlayer().wasOffsideWhenPassed()){
      // System.out.println(this.getWorld().getCycleAndPlayerString() + " I WAS
      // OFFSIDE -> no ball interception");
      // }
      return false;
    }

    if (PLAY_MODE.KICK_OFF_OWN == this.getWorld().getPlayMode() && this
        .getPlayer().getNumber() == 10) {
      return true;
    }

    // if (PLAY_MODE.META_STANDARD_OWN.contains(getWorld().getPlayMode())
    // && getProphet().getPlayerForStandard().getNumber()==
    // getWorld().getNumber()) {
    // if (getWorld().canKick()) {
    // return false;
    // }
    // return true;
    // }
    //    
    // if (PLAY_MODE.META_STANDARD_OWN.contains(getWorld().getPlayMode()) &&
    // getProphet().getPlayerForStandard().getNumber()!= getWorld().getNumber())
    // {
    // return false;
    // }

    this.myInterceptionTime = this.getProphet().getInterceptionTime(
        this.getPlayer());

    // we already control the ball or we are not even near
    if (this.myInterceptionTime == Integer.MAX_VALUE) {
      return false;
    }

    // teammates with ball don't intercept
    // the self may still intercept
    if (this.myInterceptionTime == 0) {
      if (this.getPlayer().equals(this.getWorld().getSelfRef())) {
        return true;
      }
      else {
        return false;
      }
    }

    final LinkedList<Player> fastestPlayers = this.getProphet()
        .getFastestPlayerToBall();

    final Player fastest = fastestPlayers.get(0);

    // only one player relevant
    if (fastestPlayers.size() == 1) {
      return fastest.equals(this.getPlayer());
    }

    // // loading some values //////////////

    Player fastestTeammate = null;
    Player secondFastestTeammate = null;

    for (final Player p : fastestPlayers) {
      if (p.isFriend()) {
        if (fastestTeammate == null) {
          fastestTeammate = p;
        }
        else {
          secondFastestTeammate = p;
          break;
        }
      }
    }

    // we are not among the fastest
    if (fastestTeammate == null || (!fastestTeammate.equals(this.getPlayer()) && (secondFastestTeammate == null || !secondFastestTeammate
        .equals(this.getPlayer())))) {
      return false;
    }

    // ball is heading for our goal, maybe we can still tackle?
    if (this.getProphet().ballWillGoIntoOwnGoal()) {
      return true;
    }

    // if we kicked the ball out, we don't have to hurry
    if (this.getProphet().ballOutBeforeIntercept() && this.getProphet()
        .getLastKickingPlayer() != null && this.getProphet()
        .getLastKickingPlayer().isFriend()) {
      return false;
    }

    this.fastestOpponent = this.getProphet().getFastestEnemyToBall();

    this.bestInterceptionTime = this.getProphet().getInterceptionTime();

    this.fastestTeammateInterceptionTime = this.getProphet()
        .getInterceptionTime(fastestTeammate);
    this.secondFastestTeammateInterceptionTime = (secondFastestTeammate == null) ? Integer.MAX_VALUE : this
        .getProphet().getInterceptionTime(secondFastestTeammate);
    this.opponentInterceptionTime = (this.fastestOpponent == null) ? Integer.MAX_VALUE : this
        .getProphet().getInterceptionTime(this.fastestOpponent);

    // ball is controled by someone (not the self!)
    if (this.bestInterceptionTime == 0) {
      // controlled by teammate
      if (this.fastestTeammateInterceptionTime == 0) {
        return false;
      }

      // controlled by opponent
      // if(myInterceptionTime ==1){
      // return true;
      // }

      // no interception as opponent controls ball
      return false;
    }

    // the tolerance used (the interceptiontimes might be slightly wrong due to
    // noise
    // and extrapolation)
    int toleranceOpponent = (int) (Math.pow(this.bestInterceptionTime, 0.4) + 0.35);
    
	  if (this.fastestOpponent != null && this.bestInterceptionTime != 0 && this.opponentInterceptionTime == this.bestInterceptionTime) {
	    toleranceOpponent += Math.min(this.fastestOpponent.getAge(this
	        .getWorld().getBodyCycle(), UPDATE_MODE.SEEN_NOTEAM), 3);
	    if ((this.getProphet().getNextInterceptAction(this.fastestOpponent) != null && this
	        .getProphet().getNextInterceptAction(this.fastestOpponent)
	        .getType() == TYPE.TURN) || Math.abs(this.fastestOpponent
	        .getBodyDir()) < 90) {
	      toleranceOpponent += 2;
	    }
	    if (ROLE.META_POSITION_ATTACKER.contains(this.getPlayer()
	        .getRoleByRoleNumber()) && this.getPlayer().getStamina() > 2000) {
	      toleranceOpponent += 3;
	    }
	  }


    final int toleranceTeammate = (int) (Math.pow(this.bestInterceptionTime, 0.4) + 0.35);

    // we are too slow
    if (this.myInterceptionTime > this.bestInterceptionTime + toleranceOpponent) {
      return false;
    }

    // the second teammate is too slow -> we are the one to go
    // (as we are not too slow we can't be second but must be 1st)
    if (this.secondFastestTeammateInterceptionTime > this.bestInterceptionTime + toleranceOpponent) {
      return true;
    }

    // teammates are expected to intercept for security reasons
    if (!this.getPlayer().isMe()) {
      return true;
    }

    boolean secondShouldGo = false;

    if (this.getProphet().getNextBallInterceptionPoint().y > 0) {
      return this.getPlayer().equals(fastestTeammate);
    }
    else {
      if (secondFastestTeammate.getDistance(SConf.getInstance().GOAL_POS_OWN) > fastestTeammate
          .getDistance(SConf.getInstance().GOAL_POS_OWN)) {
        secondShouldGo = true;
      }
    }

    if (this.getPlayer().equals(this.getWorld().getSelfRef()) && this
        .getPlayer().getStamina() < 1400 && this.getProphet()
        .getNextBallInterceptionPoint().y > -20) {
      return false;
    }

    // no opponent
    if (this.fastestOpponent == null) {
      if (fastest.equals(this.getPlayer())) {
        // if the second teammate is fast enough, better fitting (smaller
        // number)
        // and intercepting we don't go
        if (this.secondFastestTeammateInterceptionTime < this.bestInterceptionTime + 4 && secondShouldGo) {
          return false;
        }
        // we go otherwise
        else {
          return true;
        }
      }
      else {
        // we are not fastest but fast enough to go,
        // so we go if our number is smaller than the fastest number
        return this.getPlayer().getNumber() < fastest.getNumber() || this.secondFastestTeammateInterceptionTime <= this.bestInterceptionTime + toleranceTeammate;
      }
    }

    // opponent intercepts
    else {
      // opponent is fastest
      // FIXME
      // this is working but could be better (should be done by someone from
      // defense)
      if (this.opponentInterceptionTime == this.bestInterceptionTime) {
        return true;
      }
      // we are fastest (same logic as above with smaller tolerance)
      else {
        if (fastest.equals(this.getPlayer())) {
          if (this.secondFastestTeammateInterceptionTime + 2 < this.opponentInterceptionTime && secondShouldGo) {
            return false;
          }
          else {
            return true;
          }
        }
        else {
          return secondShouldGo || this.secondFastestTeammateInterceptionTime <= this.bestInterceptionTime + toleranceTeammate;
        }
      }
    }
  }

  /**
   * Probability is always 1.
   */
  @Override
  protected double successProbability() {

    return 1;
  }

  /**
   * Benefit is always 1.
   */
  @Override
  protected double successBenefit() {

    if (this.checkPlayerHasBall() && this.getPlayer().equals(
        this.getWorld().getSelfRef())) {
      // System.out.println("********* INTERCEPTING " +
      // this.getWorld().getCycleAndPlayerString() +
      // this.getProphet().getInterceptBallBenefit());
      return this.getProphet().getInterceptBallBenefit();
    }

    return 0.95;
  }

  /*
   * (non-Javadoc)
   * 
   * @see robocup.component.tactics.AbstractState#calculateMainAction()
   */
  @Override
  public void calculateMainAction() {

    if (this.getWorld().getPlayMode() == PLAY_MODE.PLAY_ON && (this.mainAction = this
        .getProphet().getNextInterceptAction(this.getPlayer())) != null) {

      // if ball goes out anyway we don't have to hurry
      if (this.getProphet().ballOutBeforeIntercept() && !this.getProphet()
          .ballWillGoIntoOwnGoal() && this.mainAction.getType() == TYPE.DASH) {
        this.getWorld().getSelfRef().correctDashPower(this.mainAction,
            STAMINA_LEVEL.HIGH);
      }
    }
    else {
      this.mainAction = BasicActionFactory.moveToPos(this.getWorld(), this
          .getWorld().getBallRef().getPosition(),
          ControllerNumbers.STAMINA_LEVEL.MEDIUM);
    }
  }

  /*
   * (non-Javadoc)
   * 
   * @see robocup.component.tactics.AbstractState#calculateTurnNeckAction()
   */
  @Override
  public void calculateTurnNeckAction() {

    if (this.getWorld().getBallRef().getAge(this.getWorld().getBodyCycle(),
        robocup.component.worldobjects.Ball.UPDATE_MODE.SEEN_WITHOUT_SPEED) > 2) {
      this.turnNeckAction = getProphet().getTurnNeckAction(this.state, this.mainAction, this.viewModeAction, this.getWorld()
              .getBallRef());
      return;
    }

    if (this.myInterceptionTime > this.opponentInterceptionTime - 5) {
      final double[] weights = new double[23];
      Arrays.fill(weights, 0);

      weights[22] = 1;
      if (this.fastestOpponent != null) {
        weights[this.fastestOpponent.getNumber() - 1 + 11] = 0.5;
      }

      this.turnNeckAction = getProphet().getTurnNeckAction(this.state, this.mainAction, this.viewModeAction, weights);
      return;
    }
    
    // before getting the ball watch in the direction of the opp. goal
    if (this.myInterceptionTime <= 2 && this.getWorld().getAgeOfPoint(
        SConf.getInstance().GOAL_POS_OTHER) > 1 && this.getWorld()
        .getOpponentsGoalie() != null && 52.5-getProphet().getNextBallInterceptionPoint().getY()<20) {
      final TurnNeckAction tna = TurnNeckActionFactory.watchPoint(this
          .getWorld().getSelfRef(), SConf.getInstance().GOAL_POS_OTHER);
      if (tna != null) {
//        System.out.println(getWorld().getCycleAndPlayerString()+" watch goal: "+tna);
        this.turnNeckAction = tna;
        return;
      }
    }
    else {
      super.calculateTurnNeckAction();
    }
  }

}

/**
 * $Log: InterceptBallState.java,v $ Revision 1.32 2006/11/05 13:58:02 wetzker
 * more merging - CLang + MF + some states still missing
 * 
 * Revision 1.31 2006/06/13 21:35:20 phil changed all System.err to System.out
 * 
 * Revision 1.30 2006/06/11 18:15:58 wetzker debugged collision model added
 * corner to kickNRunAlternatives
 * 
 * Revision 1.29 2006/06/07 11:01:35 wetzker *** empty log message ***
 * 
 * Revision 1.28 2006/06/05 22:41:31 wetzker avoiding dribbling at kick_off_own
 * some debugging
 * 
 * Revision 1.25 2006/06/02 22:33:11 wetzker removed some old stuff organized
 * imports
 * 
 * Revision 1.24 2006/06/01 19:34:17 wetzker added more tolerance for attackers
 * added some tolerance (but not too much :-) for defenders)
 * 
 * Revision 1.23 2006/06/01 12:25:06 endert Overworked Defense in own Penalty
 * (MarkOpponent).
 * 
 * Revision 1.22 2006/05/28 23:48:09 wetzker added clear ball to defender
 * debugged passing, clearball, tackling, attacking, kickNRun added stamina
 * control to kickNRun and waiting to dribbling
 * 
 * Revision 1.21 2006/05/28 21:29:31 gamel added player for standard
 * 
 * Revision 1.20 2006/05/27 21:58:01 wetzker added wasOffsideFlag to OwnPlayers -
 * players that were offside do not intercept better offense
 * 
 * Revision 1.19 2006/05/27 21:18:04 endert Changed security for defenders
 * 
 * Revision 1.18 2006/05/26 08:48:21 wetzker removed NPE when no opponent on
 * field
 * 
 * Revision 1.17 2006/05/24 23:37:24 wetzker improved interceptBallSit - added
 * willGoIntoOwnGoal() - getLastKickingPlayer() improved Tackling - in some
 * situations we always try to tackle now
 * 
 * Revision 1.16 2006/05/23 17:40:02 wetzker added new dribbling (still much to
 * do but better than before :-) added new role-model (see player.java) debugged
 * clear ball, kickNRun,Passing, InterceptBall, NR
 * 
 * Revision 1.15 2006/05/16 12:01:54 wetzker debugged strange attentionTo and
 * hear behaviour
 * 
 * Revision 1.14 2006/05/15 13:13:20 wetzker added new dribbling (by now only
 * wait with ball) added new clear ball adjusted interceptBall (please report
 * bugs) created new AttentionTo- und modified SayModel
 * 
 * Revision 1.13 2006/05/05 22:34:52 wetzker removed bug for heared ball pos
 * (was set to the dribbling player before the dribbling player was set) added
 * dash correction when over max player speed - happens only for heterogenous
 * very fast players (saves up to 40% stamina!!)
 * 
 * Revision 1.12 2006/05/05 16:13:58 wetzker added new attack logic for midfield
 * and attack debug some wordmodel stuff
 * 
 * Revision 1.9 2006/04/23 12:22:00 wetzker decreased pass tolerance new logic
 * in SayActionFactory more interceptOpponent
 * 
 * Revision 1.8 2006/04/22 19:30:04 wetzker removed some bugs in players and
 * ball model implemented new interceptBall logic (see state) - close ball
 * interception should work better now - backward interception should now occur
 * less often improved kickNRun Revision 1.7 2006/04/18 22:46:29 wetzker merged
 * with situationEvaluation branch new: - central situation evaluation for
 * states with ball control - kickNRun situation - ball interception possible
 * when ball kickable - role model for players 6-11(roles are shown in gui,
 * roles not used much for acting yet)
 * 
 * Revision 1.6.2.1 2006/04/17 13:35:20 wetzker *** empty log message ***
 * 
 * Revision 1.6 2006/04/06 22:58:57 wetzker (hopefully) improved ball
 * interception in defense mode - back4 gets called more often instead of
 * InterceptBall
 * 
 * Revision 1.5 2006/04/05 22:47:59 wetzker added prediction of other players
 * state each player now contains a stateEvaluation (see StateTactic) used the
 * prediction for calculation the players of the next cycle (can be seen in the
 * gui) + new State (OpponentBallInterception) for prediction of opponents
 * removed bug in delayed intercetion (cause stamina loss for nothing)
 * meta_playmodes can be used now (added always playmode) improved attack in
 * line
 * 
 * Revision 1.4 2006/03/26 21:58:41 wetzker created geometry package and moved
 * geometry classes from worldobject package there
 * 
 * Revision 1.3 2006/03/26 20:09:58 wetzker each player now contains its known
 * states and the playmodes of each state
 * 
 * Revision 1.2 2006/03/18 16:15:21 wetzker removed unused states (mainly
 * positioning) players 9-11 now use the moveBack State + new attentionTo and
 * say logic + TackleSaying
 * 
 * Revision 1.1 2006/03/13 07:36:25 axle moved from ROOT/src for Maven 2
 * Buildsystem and Team/Soccerscope separation
 * 
 * Revision 1.16 2006/03/11 10:18:15 gamel no interception when opponent has
 * ball
 * 
 * Revision 1.15 2006/03/08 22:02:19 wetzker improved precon - player now goes
 * more often to ball especially when behind the opponent or with the ball far
 * away
 * 
 * Revision 1.14 2006/03/07 12:46:15 endert Merge after refactoring: - New
 * State-Interface: One Method for each action type - Action Bean removed -
 * ActionFactorys for helper methods, States calculate main-Action themselves -
 * formatted code - added State-List in Monitor (Context-Menu of Player) - other
 * smaller stuff
 * 
 * Revision 1.13.2.3 2006/02/28 16:58:09 endert Organized imports, outsourced
 * unused vars, removed warnings!
 * 
 * Revision 1.13.2.2 2006/02/28 16:43:13 endert Changed State-Interface:
 * -resetState() -calculateXYAction() Usage of ActionFactories (no higher level
 * actions, these should be calculated in calculateMainAction()), Set
 * getAction() to deprecated and retactoring of variable names (wm ->
 * getWorld())
 * 
 * Revision 1.13.2.1 2006/02/25 22:07:20 wetzker formated
 * 
 * Revision 1.13 2006/02/24 18:53:49 wetzker used enums for Play_modes and
 * Referee_messages - still has to be improved after creation of new
 * Play_mode-Model - tagged old version removed millions of warnings
 * 
 * Revision 1.12 2006/02/22 16:04:29 endert This is done: - Rebound SoccerScope
 * to Agents (can be added or removed in config-file) - Situations now all
 * extend abstract class Situation - Refactorings: ActionBean _ removed from
 * Attributes - SoccerScope will automatically connect to host given in
 * Start-Call - Trainer uses the same host as soccerscope - Removed unused stuff
 * (old confs, bat, etc.) - Code formatted (good luck when merging) - organized
 * imports - all that i forgot to mention here Revision 1.11 2006/02/12 00:32:25
 * endert Fuck You!
 * 
 * Revision 1.10 2006/02/06 20:42:52 endert New Say-Method in Action Bean, which
 * is called after execAcion in StateEvaluation
 * 
 * Revision 1.9 2006/02/06 18:11:18 wetzker refactored, farmatted and cleaned
 * the states Revision 1.8 2005/09/09 12:36:45 endert New Defense (Branch) with
 * simple back-four merged into head!
 * 
 * Revision 1.7.2.1 2005/09/05 12:16:25 endert Branch with a new defense
 * (back-four)
 * 
 * Revision 1.7 2005/08/16 16:26:32 wetzker merged composite branch
 * 
 * Revision 1.6.2.7 2005/08/16 15:43:22 wetzker removed ball and player cloning
 * in getSelf, getBall -> should be much faster now merged with changes from
 * head
 * 
 * Revision 1.6.2.6 2005/08/14 15:21:31 wetzker improved passing improved
 * performance of inteceptBallSituation improved scoring -> now special case in
 * passSituation -> uses gaussian distribution (added package) -
 * wordmodel.Prophet - OffensiveKickState + debugging
 * 
 * Revision 1.6.2.5 2005/07/29 16:35:42 wetzker debugged some NeckRotator
 * Problems and Clustering
 * 
 * Revision 1.6.2.3 2005/07/28 11:54:53 wetzker introduced Prophet - by now only
 * used for own ballInterception - changed "Interfaces" - added
 * InterceptOpponent state (very simple Version)
 * 
 * Revision 1.6.2.2 2005/07/21 19:19:55 endert New defined isInField (with
 * reduced borders)
 * 
 * Revision 1.6.2.1 2005/07/21 11:23:06 endert Extended PreCondition: is IP
 * inside the field:
 * 
 * Revision 1.6 2005/06/30 17:51:52 bamigu Changed SuccessBenefit for
 * InterceptBallState from 1 to 0.95, because of the Intercept Opponent State
 * 
 * Revision 1.5 2005/06/30 17:37:15 bamigu *** empty log message ***
 * 
 * Revision 1.4 2005/06/29 11:45:57 gamel moved Enum STATES from ActionBean to
 * AbstractState
 * 
 * Revision 1.3 2005/06/28 08:38:29 gamel States that use ActionBean, now give
 * themselves as an argument to ActionBean
 * 
 * Revision 1.2 2005/06/15 17:03:09 wetzker removed JIAC from Head + changes in
 * framework please report bugs or shortcomings some scenarios might not work
 * because of wrong imports
 * 
 * Revision 1.1.2.1 2005/06/09 12:31:36 endert Removal of JIAC Libraries and
 * dependencies from project, restructuring the framework and improve some
 * aspects (hoping it is an improvement)
 * 
 * Revision 1.5 2005/05/26 23:47:09 wetzker added Precons that were missing
 * added preCons to composite states added punishment for turning instead of
 * dashing added punishment for low stamina when dribbling
 * 
 * Revision 1.4 2005/05/09 16:55:01 endert New State Framework: Superclass is
 * Abstract State, which is extended by SimpleState and CompositeState. All
 * Other States extend one of them. PlayMode and Conditions are (up to now) not
 * inherited, but can be set in Constructor for some states (PlayModes).
 * Evaluation of States considers postCondition as well.
 * 
 * Revision 1.3 2005/05/09 09:14:05 karlb Added CVS log tag
 * 
 */
